package com.spring;

import com.spring.anno.*;
import com.spring.beans.factory.anno.Autowired;
import com.spring.interfaces.BeanNameAware;
import com.spring.interfaces.BeanPostProcessor;
import com.spring.interfaces.InitializingBean;
import com.spring.interfaces.ObjectFactory;


import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: Asuka
 * @Description:
 * @create: 2025-04-05 01:20
 */


public class ApplicationContext {

    private Class configClass;


    //bean定义池
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionHashMap = new ConcurrentHashMap<>();

    //BeanPostProcessor 集合
    List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();


    //单例池  一级缓存
    private ConcurrentHashMap<String,Object> singletonHashMap = new ConcurrentHashMap<>();
    //二级缓存
    private ConcurrentHashMap<String,Object> earlySingletonObjects = new ConcurrentHashMap<>();
    //三级缓存
    private ConcurrentHashMap<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();



    public ApplicationContext(Class configClass){
        this.configClass = configClass;

        //扫描 获取bean 并添加到 bean定义池
        scanfAndAddBeanDefinitionMap(configClass);

        //依赖注入
        circularDependency();
    }



    private void scanfAndAddBeanDefinitionMap(Class configClass) {
        //传进来配置类   解析
        ComponentScan componentScan = (ComponentScan)configClass.getAnnotation(ComponentScan.class);

        //获取类加载器
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();

        //通过注解值获取路径
        String path = getPathByAnnotateValue(classLoader,componentScan.value());

        //获取路径下的文件
        File file = new File(path);



        if (!file.isDirectory()) {
            return;
        }
        
        File[] files = file.listFiles();

        for (File f : files) {
            String fileName = f.getAbsolutePath();
            if(!fileName.endsWith(".class")){
                continue;
            }
            String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
            className = className.replace("\\",".");


            //  添加到 beanDefinitionHashMap
            try {
                Class<?> clazz = classLoader.loadClass(className);

                if (!clazz.isAnnotationPresent(Component.class)){
                    continue;
                }

                //是否实现了BeanPostProcessor
                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                    BeanPostProcessor beanPostProcessorInstance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                    beanPostProcessorList.add(beanPostProcessorInstance);
                }

                //获取beanName
                String beanName = getBeanName(clazz);

                BeanDefinition beanDefinition = new BeanDefinition(clazz);

                //判断是否单例
                if(clazz.isAnnotationPresent(scope.class)){
                    scope scopeAnnotation = clazz.getAnnotation(scope.class);
                    //不是单例
                    if (!BeanDefinition.SCOPE_SINGLETON.equals(scopeAnnotation.value())) {
                        beanDefinition.toPrototype();
                    }else{
                        beanDefinition.toSingleton();
                    }
                }


                beanDefinitionHashMap.put(beanName,beanDefinition);



            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }


        }






        /*//传进来配置类   解析
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        //获取扫描路径
        String path = componentScan.value();
        path = path.replace(".","/");

        //扫描
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);

        // /D:/JavaProect/spring/minispring/target/classes/com/sumika/service
        //去除开头的 /
        String filePath = resource.getFile();
        if(filePath.startsWith("/")){
            filePath = filePath.substring(1);
        }

        //获取路径下的文件
        File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                if(!fileName.endsWith(".class")){
                    continue;
                }

                String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                className = className.replace("\\",".");

                //  添加到 beanDefinitionHashMap
                try {
                    Class<?> clazz = classLoader.loadClass(className);
                    if (clazz.isAnnotationPresent(Component.class)){
                        //是否实现了BeanPostProcessor
                        if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                            BeanPostProcessor beanPostProcessorInstance = (BeanPostProcessor)clazz.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(beanPostProcessorInstance);
                        }

                        //获取bean的名字
                        String beanName = getBeanName(clazz);

                        BeanDefinition beanDefinition = new BeanDefinition();
                        //判断是否单例
                        if(clazz.isAnnotationPresent(scope.class)){
                            scope scopeAnnotation = clazz.getDeclaredAnnotation(scope.class);
                            beanDefinition.setScope(scopeAnnotation.value());
                        }else{
                            beanDefinition.setScope(Constant.SINGLETON);
                        }
                        beanDefinition.setBeanClass(clazz);
                        beanDefinitionHashMap.put(beanName,beanDefinition);

                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (InstantiationException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }

            }
        }*/


        //将定义bean池里单例的创建到单例池里
        /*beanDefinitionHashMap.forEach((beanName,beanDefinition)-> {
            if (beanDefinition.getScope().equals(Constant.SINGLETON)){
                Object bean = createBean(beanDefinition);
                singletonHashMap.put(beanName,bean);
            }
        });*/
    }

    /*
    * 通过注解值 获取 文件路径
    * */
    private String getPathByAnnotateValue(ClassLoader classLoader,String componentScanValue) {
        String path = componentScanValue;
        path = path.replace(".","/");

        //扫描
        URL resource = classLoader.getResource(path);

        // /D:/JavaProect/spring/minispring/target/classes/com/sumika/service
        //去除开头的 /
        String filePath = resource.getFile();
        if(filePath.startsWith("/")){
            filePath = filePath.substring(1);
        }
        return filePath;
    }

    public Object cacheQuery(String beanName,Class clazz) throws Exception {
        if(singletonHashMap.containsKey(beanName)){
            return singletonHashMap.get(beanName);
        } else if (earlySingletonObjects.containsKey(beanName)) {
            return earlySingletonObjects.get(beanName);
        } else if (singletonFactories.containsKey(beanName)) {
            Object singletonFactoriesObject = singletonFactories.get(beanName).getObject();
            singletonFactories.remove(beanName);
            earlySingletonObjects.put(beanName,singletonFactoriesObject);
            return singletonFactoriesObject;
        }

        //是否实现了BeanPostProcessor
        /*if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
            BeanPostProcessor beanPostProcessorInstance = (BeanPostProcessor)clazz.getDeclaredConstructor().newInstance();
            beanPostProcessorList.add(beanPostProcessorInstance);
        }*/

        Object instance = clazz.getDeclaredConstructor().newInstance();

        // Aware回调 设置beanName
        if(instance instanceof BeanNameAware){
            ((BeanNameAware)(instance)).setBeanName(beanName);
        }


        //BeanPostprocessor 初始化前调用
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
        }

        //初始化
        if(instance instanceof InitializingBean){
            try {
                ((InitializingBean)(instance)).afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        //BeanPostprocessor 初始化后调用
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
        }

        Object finalInstance = instance;
        singletonFactories.put(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws Exception {
                return finalInstance;
            }
        });

        //属性填充
        for (Field field : instance.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                Class<?> fieldClazz = field.getType();
                String fieldName = getBeanName(fieldClazz);
                Object fieldInstance = cacheQuery(fieldName, fieldClazz);
                field.set(instance,fieldInstance);
            }
        }

        //缓存转移
        singletonHashMap.put(beanName,instance);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);

        return instance;
    }


    private void circularDependency() {
        //遍历所有bean定义的类,创建对象
        beanDefinitionHashMap.forEach((beanName,beanDefinition)->{
            Class clazz = beanDefinition.getBeanClass();
            try {
                cacheQuery(beanName,clazz);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }




    private static String getBeanName(Class<?> clazz) {
        Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
        String beanName = componentAnnotation.value();

        if(beanName.isEmpty()){
            //类短名首字母小写
            beanName = Introspector.decapitalize(clazz.getSimpleName());
        }

        return beanName;
    }





    public Object getBean(String beanName) throws Exception {
        if(singletonHashMap.containsKey(beanName)){
            return singletonHashMap.get(beanName);
        } else if (earlySingletonObjects.containsKey(beanName)) {
            return earlySingletonObjects.get(beanName);
        } else if (singletonFactories.containsKey(beanName)) {
            Object singletonFactoriesObject = singletonFactories.get(beanName).getObject();
            return singletonFactoriesObject;
        }
        return null;

        //判断是否存在
        /*if(!beanDefinitionHashMap.containsKey(beanName)){
            throw new NullPointerException();
        }


        BeanDefinition beanDefinition = beanDefinitionHashMap.get(beanName);
        //判断是否单例
        if (beanDefinition.getScope().equals(Constant.SINGLETON)) {
            return singletonHashMap.get(beanName);
        }

        return createBean(beanDefinition);*/
    }

    /*public Object createBean(BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getType();

        //通过反射创建对应bean对象
        try {

            //是否实现了BeanPostProcessor
            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                BeanPostProcessor beanPostProcessorInstance = (BeanPostProcessor)clazz.getDeclaredConstructor().newInstance();
                beanPostProcessorList.add(beanPostProcessorInstance);
            }

            Object instance = clazz.getDeclaredConstructor().newInstance();

            //依赖注入
            for (Field field : instance.getClass().getDeclaredFields()) {
                if (!field.isAnnotationPresent(Autowired.class)) {
                    continue;
                }

                Class<?> type = field.getType();
                String beanName = getBeanName(type);
                field.setAccessible(true);
                Object bean = getBean(beanName);

                field.set(instance,bean);
            }


            String beanName = getBeanName(instance.getClass());

            // Aware回调 设置beanName
            if(instance instanceof BeanNameAware){
                ((BeanNameAware)(instance)).setBeanName(beanName);
            }


            //BeanPostprocessor 初始化前调用
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }

            //初始化
            if(instance instanceof InitializingBean){
                try {
                    ((InitializingBean)(instance)).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


            //BeanPostprocessor 初始化后调用
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }


            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }*/


}
